useState๋ก React ์ ํ๋ฆฌ์ผ์ด์ ์ ์ต์ ํํ์ธ์. ํจ์จ์ ์ธ ์ํ ๊ด๋ฆฌ์ ์ฑ๋ฅ ํฅ์์ ์ํ ๊ณ ๊ธ ๊ธฐ์ ์ ๋ฐฐ์๋ณด์ธ์.
React useState: ์ํ Hook ์ต์ ํ ์ ๋ต ๋ง์คํฐํ๊ธฐ
useState Hook์ React์์ ์ปดํฌ๋ํธ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํ ๊ธฐ๋ณธ์ ์ธ ๊ตฌ์ฑ ์์์
๋๋ค. ๋งค์ฐ ๋ค์ฌ๋ค๋ฅํ๊ณ ์ฌ์ฉํ๊ธฐ ์ฝ์ง๋ง, ๋ถ์ ์ ํ๊ฒ ์ฌ์ฉํ๋ฉด ํนํ ๋ณต์กํ ์ ํ๋ฆฌ์ผ์ด์
์์ ์ฑ๋ฅ ๋ณ๋ชฉ ํ์์ ์ด๋ํ ์ ์์ต๋๋ค. ์ด ์ข
ํฉ ๊ฐ์ด๋์์๋ React ์ ํ๋ฆฌ์ผ์ด์
์ ์ฑ๋ฅ๊ณผ ์ ์ง๋ณด์์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํด useState๋ฅผ ์ต์ ํํ๋ ๊ณ ๊ธ ์ ๋ต์ ํ๊ตฌํฉ๋๋ค.
useState์ ์ดํด์ ๊ทธ ์ํฅ
์ต์ ํ ๊ธฐ๋ฒ์ ์ดํด๋ณด๊ธฐ ์ ์ useState์ ๊ธฐ๋ณธ์ ๋ค์ ์ง์ด๋ณด๊ฒ ์ต๋๋ค. useState Hook์ ํจ์ํ ์ปดํฌ๋ํธ๊ฐ ์ํ๋ฅผ ๊ฐ์ง ์ ์๋๋ก ํฉ๋๋ค. ์ด Hook์ ์ํ ๋ณ์์ ๊ทธ ๋ณ์๋ฅผ ์
๋ฐ์ดํธํ๋ ํจ์๋ฅผ ๋ฐํํฉ๋๋ค. ์ํ๊ฐ ์
๋ฐ์ดํธ๋ ๋๋ง๋ค ์ปดํฌ๋ํธ๋ ๋ค์ ๋ ๋๋ง๋ฉ๋๋ค.
๊ธฐ๋ณธ ์์ :
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
Count: {count}
);
}
export default Counter;
์ด ๊ฐ๋จํ ์์ ์์ "Increment" ๋ฒํผ์ ํด๋ฆญํ๋ฉด count ์ํ๊ฐ ์
๋ฐ์ดํธ๋๊ณ , Counter ์ปดํฌ๋ํธ์ ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํฉ๋๋ค. ์ด๋ ์์ ์ปดํฌ๋ํธ์์๋ ์๋ฒฝํ๊ฒ ์๋ํ์ง๋ง, ๋๊ท๋ชจ ์ ํ๋ฆฌ์ผ์ด์
์์ ์ ์ด๋์ง ์๋ ๋ฆฌ๋ ๋๋ง์ ์ฑ๋ฅ์ ์ฌ๊ฐํ ์ํฅ์ ๋ฏธ์น ์ ์์ต๋๋ค.
useState๋ฅผ ์ต์ ํํด์ผ ํ๋ ์ด์
๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ React ์ ํ๋ฆฌ์ผ์ด์
์ฑ๋ฅ ๋ฌธ์ ์ ์ฃผ๋ฒ์
๋๋ค. ๋ชจ๋ ๋ฆฌ๋ ๋๋ง์ ๋ฆฌ์์ค๋ฅผ ์๋นํ๋ฉฐ, ๋๋ฆฐ ์ฌ์ฉ์ ๊ฒฝํ์ผ๋ก ์ด์ด์ง ์ ์์ต๋๋ค. useState๋ฅผ ์ต์ ํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ด์ ์ด ์์ต๋๋ค:
- ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง ๊ฐ์: ์ปดํฌ๋ํธ์ ์ํ๊ฐ ์ค์ ๋ก ๋ณ๊ฒฝ๋์ง ์์์ ๋ ๋ฆฌ๋ ๋๋ง๋๋ ๊ฒ์ ๋ฐฉ์งํฉ๋๋ค.
- ์ฑ๋ฅ ํฅ์: ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ ๋น ๋ฅด๊ณ ๋ฐ์์ฑ ์๊ฒ ๋ง๋ญ๋๋ค.
- ์ ์ง๋ณด์์ฑ ์ฆ์ง: ๋ ๊น๋ํ๊ณ ํจ์จ์ ์ธ ์ฝ๋๋ฅผ ์์ฑํฉ๋๋ค.
์ต์ ํ ์ ๋ต 1: ํจ์ํ ์ ๋ฐ์ดํธ
์ด์ ์ํ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ํ๋ฅผ ์
๋ฐ์ดํธํ ๋๋ ํญ์ setCount์ ํจ์ํ ์
๋ฐ์ดํธ๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค. ์ด๋ ์ค๋๋ ํด๋ก์ (stale closure) ๋ฌธ์ ๋ฅผ ๋ฐฉ์งํ๊ณ ํญ์ ์ต์ ์ํ ๊ฐ์ผ๋ก ์์
ํ ์ ์๋๋ก ๋ณด์ฅํฉ๋๋ค.
์๋ชป๋ ๋ฐฉ๋ฒ (๋ฌธ์ ๊ฐ ๋ ์ ์์):
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setTimeout(() => {
setCount(count + 1); // ์ค๋๋ 'count' ๊ฐ์ผ ์ ์์
}, 1000);
};
return (
Count: {count}
);
}
์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ (ํจ์ํ ์ ๋ฐ์ดํธ):
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setTimeout(() => {
setCount(prevCount => prevCount + 1); // ์ ํํ 'count' ๊ฐ์ ๋ณด์ฅ
}, 1000);
};
return (
Count: {count}
);
}
setCount(prevCount => prevCount + 1)๋ฅผ ์ฌ์ฉํ๋ฉด setCount์ ํจ์๋ฅผ ์ ๋ฌํ๊ฒ ๋ฉ๋๋ค. ๊ทธ๋ฌ๋ฉด React๋ ์ํ ์
๋ฐ์ดํธ๋ฅผ ํ์ ๋ฃ๊ณ ๊ฐ์ฅ ์ต์ ์ํ ๊ฐ์ผ๋ก ํด๋น ํจ์๋ฅผ ์คํํ์ฌ ์ค๋๋ ํด๋ก์ ๋ฌธ์ ๋ฅผ ํผํ ์ ์์ต๋๋ค.
์ต์ ํ ์ ๋ต 2: ๋ถ๋ณ์ฑ์ ์งํค๋ ์ํ ์ ๋ฐ์ดํธ
์ํ์์ ๊ฐ์ฒด๋ ๋ฐฐ์ด์ ๋ค๋ฃฐ ๋๋ ํญ์ ๋ถ๋ณ์ฑ์ ์ง์ผ ์ ๋ฐ์ดํธํด์ผ ํฉ๋๋ค. ์ํ๋ฅผ ์ง์ ๋ณ๊ฒฝ(mutate)ํ๋ฉด React๊ฐ ์ฐธ์กฐ ๋์ผ์ฑ(referential equality)์ ์ฌ์ฉํ์ฌ ๋ณ๊ฒฝ ์ฌํญ์ ๊ฐ์งํ๊ธฐ ๋๋ฌธ์ ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ์ง ์์ต๋๋ค. ๋์ , ์ํ๋ ์์ ์ฌํญ์ ์ ์ฉํ ๊ฐ์ฒด๋ ๋ฐฐ์ด์ ์๋ก์ด ๋ณต์ฌ๋ณธ์ ๋ง๋ค์ด์ผ ํฉ๋๋ค.
์๋ชป๋ ๋ฐฉ๋ฒ (์ํ ์ง์ ๋ณ๊ฒฝ):
function ShoppingCart() {
const [items, setItems] = useState([{ id: 1, name: 'Apple', quantity: 2 }]);
const updateQuantity = (id, newQuantity) => {
const item = items.find(item => item.id === id);
if (item) {
item.quantity = newQuantity; // ์ง์ ๋ณ๊ฒฝ! ๋ฆฌ๋ ๋๋ง์ ์ ๋ฐํ์ง ์์.
setItems(items); // React๊ฐ ๋ณ๊ฒฝ์ ๊ฐ์งํ์ง ๋ชปํด ๋ฌธ์ ๊ฐ ๋ฐ์ํจ.
}
};
return (
{items.map(item => (
{item.name} - Quantity: {item.quantity}
))}
);
}
์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ (๋ถ๋ณ์ฑ์ ์งํจ ์ ๋ฐ์ดํธ):
function ShoppingCart() {
const [items, setItems] = useState([{ id: 1, name: 'Apple', quantity: 2 }]);
const updateQuantity = (id, newQuantity) => {
setItems(prevItems =>
prevItems.map(item =>
item.id === id ? { ...item, quantity: newQuantity } : item
)
);
};
return (
{items.map(item => (
{item.name} - Quantity: {item.quantity}
))}
);
}
์์ ๋ ๋ฒ์ ์์๋ .map()์ ์ฌ์ฉํ์ฌ ์
๋ฐ์ดํธ๋ ํญ๋ชฉ์ด ํฌํจ๋ ์ ๋ฐฐ์ด์ ๋ง๋ญ๋๋ค. ์ ๊ฐ ์ฐ์ฐ์(...item)๋ฅผ ์ฌ์ฉํ์ฌ ๊ธฐ์กด ์์ฑ์ ๊ฐ์ง ์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ณ quantity ์์ฑ์ ์ ๊ฐ์ผ๋ก ๋ฎ์ด์๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด setItems๊ฐ ์ ๋ฐฐ์ด์ ๋ฐ์ ๋ฆฌ๋ ๋๋ง์ ์ ๋ฐํ๊ณ UI๋ฅผ ์
๋ฐ์ดํธํ๊ฒ ๋ฉ๋๋ค.
์ต์ ํ ์ ๋ต 3: ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ํผํ๊ธฐ ์ํ `useMemo` ์ฌ์ฉ
useMemo hook์ ๊ณ์ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์
(memoization)ํ๋ ๋ฐ ์ฌ์ฉ๋ ์ ์์ต๋๋ค. ์ด๋ ๊ณ์ฐ ๋น์ฉ์ด ๋น์ธ๊ณ ํน์ ์ํ ๋ณ์์๋ง ์์กดํ ๋ ์ ์ฉํฉ๋๋ค. ํด๋น ์ํ ๋ณ์๊ฐ ๋ณ๊ฒฝ๋์ง ์์๋ค๋ฉด useMemo๋ ์บ์๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ์ฌ ๊ณ์ฐ์ด ๋ค์ ์คํ๋๋ ๊ฒ์ ๋ง๊ณ ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ํผํ ์ ์์ต๋๋ค.
์์ :
import React, { useState, useMemo } from 'react';
function ExpensiveComponent({ data }) {
const [multiplier, setMultiplier] = useState(2);
// 'data'์ 'multiplier'์๋ง ์์กดํ๋ ๋น์ฉ์ด ํฐ ๊ณ์ฐ
const processedData = useMemo(() => {
console.log('๋ฐ์ดํฐ ์ฒ๋ฆฌ ์ค...');
// ๋น์ฉ์ด ํฐ ์ฐ์ฐ ์๋ฎฌ๋ ์ด์
let result = data.map(item => item * multiplier);
return result;
}, [data, multiplier]);
return (
Processed Data: {processedData.join(', ')}
);
}
function App() {
const [data, setData] = useState([1, 2, 3, 4, 5]);
return (
);
}
export default App;
์ด ์์ ์์ processedData๋ data๋ multiplier๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง ๋ค์ ๊ณ์ฐ๋ฉ๋๋ค. ๋ง์ฝ ExpensiveComponent์ ๋ค๋ฅธ ์ํ ๋ถ๋ถ์ด ๋ณ๊ฒฝ๋๋๋ผ๋ ์ปดํฌ๋ํธ๋ ๋ฆฌ๋ ๋๋ง๋์ง๋ง, processedData๋ ๋ค์ ๊ณ์ฐ๋์ง ์์ ์ฒ๋ฆฌ ์๊ฐ์ ์ ์ฝํ ์ ์์ต๋๋ค.
์ต์ ํ ์ ๋ต 4: ํจ์๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์ ํ๊ธฐ ์ํ `useCallback` ์ฌ์ฉ
useMemo์ ์ ์ฌํ๊ฒ, useCallback์ ํจ์๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์
ํฉ๋๋ค. ์ด๋ ์์ ์ปดํฌ๋ํธ์ ํจ์๋ฅผ props๋ก ์ ๋ฌํ ๋ ํนํ ์ ์ฉํฉ๋๋ค. useCallback์ด ์์ผ๋ฉด ๋ ๋๋ง๋ง๋ค ์๋ก์ด ํจ์ ์ธ์คํด์ค๊ฐ ์์ฑ๋์ด, props๊ฐ ์ค์ ๋ก ๋ณ๊ฒฝ๋์ง ์์์์๋ ์์ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง๋ ์ ์์ต๋๋ค. ์ด๋ React๊ฐ ์๊ฒฉํ ๋๋ฑ์ฑ(===)์ ์ฌ์ฉํ์ฌ props์ ์ฐจ์ด๋ฅผ ํ์ธํ๋๋ฐ, ์๋ก์ด ํจ์๋ ํญ์ ์ด์ ํจ์์ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์
๋๋ค.
์์ :
import React, { useState, useCallback } from 'react';
const Button = React.memo(({ onClick, children }) => {
console.log('Button ๋ ๋๋ง๋จ');
return ;
});
function ParentComponent() {
const [count, setCount] = useState(0);
// increment ํจ์๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์
const increment = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []); // ๋น ์์กด์ฑ ๋ฐฐ์ด์ ์ด ํจ์๊ฐ ํ ๋ฒ๋ง ์์ฑ๋จ์ ์๋ฏธ
return (
Count: {count}
);
}
export default ParentComponent;
์ด ์์ ์์๋ increment ํจ์๊ฐ ๋น ์์กด์ฑ ๋ฐฐ์ด๊ณผ ํจ๊ป useCallback์ ์ฌ์ฉํ์ฌ ๋ฉ๋ชจ์ด์ ์ด์
๋์์ต๋๋ค. ์ด๋ ํจ์๊ฐ ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ๋ ๋ ํ ๋ฒ๋ง ์์ฑ๋๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. Button ์ปดํฌ๋ํธ๋ React.memo๋ก ๊ฐ์ธ์ ธ ์์ผ๋ฏ๋ก props๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง ๋ฆฌ๋ ๋๋ง๋ฉ๋๋ค. increment ํจ์๋ ๋ชจ๋ ๋ ๋๋ง์์ ๋์ผํ๋ฏ๋ก Button ์ปดํฌ๋ํธ๋ ๋ถํ์ํ๊ฒ ๋ฆฌ๋ ๋๋ง๋์ง ์์ต๋๋ค.
์ต์ ํ ์ ๋ต 5: ํจ์ํ ์ปดํฌ๋ํธ๋ฅผ ์ํ `React.memo` ์ฌ์ฉ
React.memo๋ ํจ์ํ ์ปดํฌ๋ํธ๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์
ํ๋ ๊ณ ์ฐจ ์ปดํฌ๋ํธ(HOC)์
๋๋ค. props๊ฐ ๋ณ๊ฒฝ๋์ง ์์๋ค๋ฉด ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง๋๋ ๊ฒ์ ๋ฐฉ์งํฉ๋๋ค. ์ด๋ props์๋ง ์์กดํ๋ ์์ ์ปดํฌ๋ํธ(pure component)์ ํนํ ์ ์ฉํฉ๋๋ค.
์์ :
import React from 'react';
const MyComponent = React.memo(({ name }) => {
console.log('MyComponent ๋ ๋๋ง๋จ');
return Hello, {name}!
;
});
export default MyComponent;
React.memo๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ฌ์ฉํ๋ ค๋ฉด ์ปดํฌ๋ํธ๊ฐ ์์(pure)ํ์ง ํ์ธํด์ผ ํฉ๋๋ค. ์ฆ, ๋์ผํ ์
๋ ฅ props์ ๋ํด ํญ์ ๋์ผํ ์ถ๋ ฅ์ ๋ ๋๋งํด์ผ ํฉ๋๋ค. ๋ง์ฝ ์ปดํฌ๋ํธ์ ๋ถ์ ํจ๊ณผ(side effect)๊ฐ ์๊ฑฐ๋ ๋ณ๊ฒฝ๋ ์ ์๋ ์ปจํ
์คํธ์ ์์กดํ๋ค๋ฉด React.memo๋ ์ต์์ ํด๊ฒฐ์ฑ
์ด ์๋ ์ ์์ต๋๋ค.
์ต์ ํ ์ ๋ต 6: ๊ฑฐ๋ ์ปดํฌ๋ํธ ๋ถ๋ฆฌํ๊ธฐ
๋ณต์กํ ์ํ๋ฅผ ๊ฐ์ง ๊ฑฐ๋ ์ปดํฌ๋ํธ๋ ์ฑ๋ฅ ๋ณ๋ชฉ ํ์์ ์์ธ์ด ๋ ์ ์์ต๋๋ค. ์ด๋ฌํ ์ปดํฌ๋ํธ๋ฅผ ๋ ์๊ณ ๊ด๋ฆฌํ๊ธฐ ์ฌ์ด ๋จ์๋ก ๋ถ๋ฆฌํ๋ฉด ๋ฆฌ๋ ๋๋ง์ ๊ตญ์ํํ์ฌ ์ฑ๋ฅ์ ํฅ์์ํฌ ์ ์์ต๋๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์ํ์ ์ผ๋ถ๊ฐ ๋ณ๊ฒฝ๋ ๋, ๊ฑฐ๋ ์ปดํฌ๋ํธ ์ ์ฒด๊ฐ ์๋ ๊ด๋ จ๋ ํ์ ์ปดํฌ๋ํธ๋ง ๋ฆฌ๋ ๋๋งํ๋ฉด ๋ฉ๋๋ค.
์์ (๊ฐ๋ ์ ):
์ฌ์ฉ์ ์ ๋ณด์ ํ๋ ํผ๋๋ฅผ ๋ชจ๋ ์ฒ๋ฆฌํ๋ ๊ฑฐ๋ํ UserProfile ์ปดํฌ๋ํธ ํ๋๋ฅผ ๋๋ ๋์ , UserInfo์ ActivityFeed ๋ ๊ฐ์ ์ปดํฌ๋ํธ๋ก ๋ถ๋ฆฌํ์ธ์. ๊ฐ ์ปดํฌ๋ํธ๋ ์์ ๋ง์ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ฉฐ, ์์ ์ ํน์ ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง ๋ฆฌ๋ ๋๋ง๋ฉ๋๋ค.
์ต์ ํ ์ ๋ต 7: ๋ณต์กํ ์ํ ๋ก์ง์ ์ํ `useReducer`์ ๋ฆฌ๋์ ์ฌ์ฉ
๋ณต์กํ ์ํ ์ ํ์ ๋ค๋ฃฐ ๋ useReducer๋ useState์ ๊ฐ๋ ฅํ ๋์์ด ๋ ์ ์์ต๋๋ค. ์ด๋ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ๋ ๊ตฌ์กฐํ๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋ฉฐ ์ข
์ข
๋ ๋์ ์ฑ๋ฅ์ผ๋ก ์ด์ด์ง ์ ์์ต๋๋ค. useReducer hook์ ์ฌ๋ฌ ํ์ ๊ฐ์ ํฌํจํ๊ณ ์ก์
์ ๋ฐ๋ผ ์ธ๋ถํ๋ ์
๋ฐ์ดํธ๊ฐ ํ์ํ ๋ณต์กํ ์ํ ๋ก์ง์ ๊ด๋ฆฌํฉ๋๋ค.
์์ :
import React, { useReducer } from 'react';
const initialState = { count: 0, theme: 'light' };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'decrement':
return { ...state, count: state.count - 1 };
case 'toggleTheme':
return { ...state, theme: state.theme === 'light' ? 'dark' : 'light' };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
Count: {state.count}
Theme: {state.theme}
);
}
export default Counter;
์ด ์์ ์์ reducer ํจ์๋ ์ํ๋ฅผ ์
๋ฐ์ดํธํ๋ ๋ค์ํ ์ก์
์ ์ฒ๋ฆฌํฉ๋๋ค. useReducer๋ ๋ ๋๋ง ์ต์ ํ์๋ ๋์์ด ๋ ์ ์๋๋ฐ, ์ด๋ ์ฌ๋ฌ ๊ฐ์ useState hook์ผ๋ก ์ธํด ๋ฐ์ํ ์ ์๋ ๊ด๋ฒ์ํ ๋ฆฌ๋ ๋๋ง๊ณผ ๋น๊ตํ์ฌ, ๋ฉ๋ชจ์ด์ ์ด์
์ ํตํด ์ํ์ ์ด๋ค ๋ถ๋ถ์ด ์ปดํฌ๋ํธ ๋ ๋๋ง์ ์ ๋ฐํ ์ง ์ ์ดํ ์ ์๊ธฐ ๋๋ฌธ์
๋๋ค.
์ต์ ํ ์ ๋ต 8: ์ ํ์ ์ํ ์ ๋ฐ์ดํธ
๋๋ก๋ ์ปดํฌ๋ํธ์ ์ฌ๋ฌ ์ํ ๋ณ์๊ฐ ์์ง๋ง, ๊ทธ ์ค ์ผ๋ถ๋ง ๋ณ๊ฒฝ๋ ๋ ๋ฆฌ๋ ๋๋ง์ ์ ๋ฐํ ์ ์์ต๋๋ค. ์ด๋ฐ ๊ฒฝ์ฐ, ์ฌ๋ฌ useState hook์ ์ฌ์ฉํ์ฌ ์ ํ์ ์ผ๋ก ์ํ๋ฅผ ์
๋ฐ์ดํธํ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ์ค์ ๋ก ์
๋ฐ์ดํธ๊ฐ ํ์ํ ์ปดํฌ๋ํธ ๋ถ๋ถ์๋ง ๋ฆฌ๋ ๋๋ง์ ๊ตญํ์ํฌ ์ ์์ต๋๋ค.
์์ :
import React, { useState } from 'react';
function MyComponent() {
const [name, setName] = useState('John');
const [age, setAge] = useState(30);
const [location, setLocation] = useState('New York');
// ์์น๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง ์์น๋ฅผ ์
๋ฐ์ดํธ
const handleLocationChange = (newLocation) => {
setLocation(newLocation);
};
return (
Name: {name}
Age: {age}
Location: {location}
);
}
export default MyComponent;
์ด ์์ ์์ location์ ๋ณ๊ฒฝํ๋ฉด location์ ํ์ํ๋ ์ปดํฌ๋ํธ ๋ถ๋ถ๋ง ๋ฆฌ๋ ๋๋ง๋ฉ๋๋ค. name๊ณผ age ์ํ ๋ณ์๋ ๋ช
์์ ์ผ๋ก ์
๋ฐ์ดํธ๋์ง ์๋ ํ ์ปดํฌ๋ํธ๋ฅผ ๋ฆฌ๋ ๋๋งํ์ง ์์ต๋๋ค.
์ต์ ํ ์ ๋ต 9: ๋๋ฐ์ด์ฑ๊ณผ ์ค๋กํ๋ง์ ์ด์ฉํ ์ํ ์ ๋ฐ์ดํธ
์ํ ์ ๋ฐ์ดํธ๊ฐ ๋น๋ฒํ๊ฒ ๋ฐ์ํ๋ ์๋๋ฆฌ์ค(์: ์ฌ์ฉ์ ์ ๋ ฅ ์ค)์์๋ ๋๋ฐ์ด์ฑ(debouncing)๊ณผ ์ค๋กํ๋ง(throttling)์ด ๋ฆฌ๋ ๋๋ง ํ์๋ฅผ ์ค์ด๋ ๋ฐ ๋์์ด ๋ ์ ์์ต๋๋ค. ๋๋ฐ์ด์ฑ์ ํจ์๊ฐ ๋ง์ง๋ง์ผ๋ก ํธ์ถ๋ ํ ์ผ์ ์๊ฐ์ด ์ง๋ ๋๊น์ง ํจ์ ํธ์ถ์ ์ง์ฐ์ํต๋๋ค. ์ค๋กํ๋ง์ ์ฃผ์ด์ง ์๊ฐ ๋์ ํจ์๊ฐ ํธ์ถ๋ ์ ์๋ ํ์๋ฅผ ์ ํํฉ๋๋ค.
์์ (๋๋ฐ์ด์ฑ):
import React, { useState, useCallback } from 'react';
import debounce from 'lodash.debounce'; // lodash ์ค์น: npm install lodash
function SearchComponent() {
const [searchTerm, setSearchTerm] = useState('');
const debouncedSetSearchTerm = useCallback(
debounce((text) => {
setSearchTerm(text);
console.log('๊ฒ์์ด ์
๋ฐ์ดํธ:', text);
}, 300),
[]
);
const handleInputChange = (event) => {
debouncedSetSearchTerm(event.target.value);
};
return (
Searching for: {searchTerm}
);
}
export default SearchComponent;
์ด ์์ ์์๋ Lodash์ debounce ํจ์๋ฅผ ์ฌ์ฉํ์ฌ setSearchTerm ํจ์ ํธ์ถ์ 300๋ฐ๋ฆฌ์ด ์ง์ฐ์ํต๋๋ค. ์ด๋ ๋ชจ๋ ํค ์
๋ ฅ๋ง๋ค ์ํ๊ฐ ์
๋ฐ์ดํธ๋๋ ๊ฒ์ ๋ฐฉ์งํ์ฌ ๋ฆฌ๋ ๋๋ง ํ์๋ฅผ ์ค์ฌ์ค๋๋ค.
์ต์ ํ ์ ๋ต 10: ๋น์ฐจ๋จ UI ์ ๋ฐ์ดํธ๋ฅผ ์ํ `useTransition` ์ฌ์ฉ
๋ฉ์ธ ์ค๋ ๋๋ฅผ ์ฐจ๋จํ๊ณ UI ์ ์ง๋ฅผ ์ ๋ฐํ ์ ์๋ ์์
์ ๊ฒฝ์ฐ, useTransition hook์ ์ฌ์ฉํ์ฌ ์ํ ์
๋ฐ์ดํธ๋ฅผ ๊ธด๊ธํ์ง ์์ ๊ฒ์ผ๋ก ํ์ํ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ฉด React๋ ๊ธด๊ธํ์ง ์์ ์ํ ์
๋ฐ์ดํธ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ ์ ์ฌ์ฉ์ ์ํธ์์ฉ๊ณผ ๊ฐ์ ๋ค๋ฅธ ์์
์ ์ฐ์ ์ ์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค. ์ด๋ ๊ณ์ฐ ์ง์ฝ์ ์ธ ์์
์ ์ฒ๋ฆฌํ ๋์๋ ๋ ๋ถ๋๋ฌ์ด ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํฉ๋๋ค.
์์ :
import React, { useState, useTransition } from 'react';
function MyComponent() {
const [isPending, startTransition] = useTransition();
const [data, setData] = useState([]);
const loadData = () => {
startTransition(() => {
// API์์ ๋ฐ์ดํฐ ๋ก๋ฉ ์๋ฎฌ๋ ์ด์
setTimeout(() => {
setData([1, 2, 3, 4, 5]);
}, 1000);
});
};
return (
{isPending && Loading data...
}
{data.length > 0 && Data: {data.join(', ')}
}
);
}
export default MyComponent;
์ด ์์ ์์ startTransition ํจ์๋ setData ํธ์ถ์ ๊ธด๊ธํ์ง ์์ ๊ฒ์ผ๋ก ํ์ํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. ๊ทธ๋ฌ๋ฉด React๋ ์ํ ์
๋ฐ์ดํธ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ ์ ๋ก๋ฉ ์ํ๋ฅผ ๋ฐ์ํ๋๋ก UI๋ฅผ ์
๋ฐ์ดํธํ๋ ๋ฑ ๋ค๋ฅธ ์์
์ ์ฐ์ ์ ์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค. isPending ํ๋๊ทธ๋ ์ ํ์ด ์งํ ์ค์ธ์ง ์ฌ๋ถ๋ฅผ ๋ํ๋
๋๋ค.
๊ณ ๊ธ ๊ณ ๋ ค์ฌํญ: ์ปจํ ์คํธ์ ์ ์ญ ์ํ ๊ด๋ฆฌ
๊ณต์ ์ํ๊ฐ ์๋ ๋ณต์กํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒฝ์ฐ React ์ปจํ ์คํธ(Context)๋ Redux, Zustand, Jotai์ ๊ฐ์ ์ ์ญ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๊ณ ๋ คํด ๋ณด์ธ์. ์ด๋ฌํ ์๋ฃจ์ ์ ์ปดํฌ๋ํธ๊ฐ ํ์ํ ์ํ์ ํน์ ๋ถ๋ถ๋ง ๊ตฌ๋ ํ๋๋ก ํ์ฉํ์ฌ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ณ ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ๋ฐฉ์งํ๋ ๋ ํจ์จ์ ์ธ ๋ฐฉ๋ฒ์ ์ ๊ณตํ ์ ์์ต๋๋ค.
๊ฒฐ๋ก
useState๋ฅผ ์ต์ ํํ๋ ๊ฒ์ ์ฑ๋ฅ์ด ๋ฐ์ด๋๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ข์ React ์ ํ๋ฆฌ์ผ์ด์
์ ๊ตฌ์ถํ๋ ๋ฐ ๋งค์ฐ ์ค์ํฉ๋๋ค. ์ํ ๊ด๋ฆฌ์ ๋ฏธ๋ฌํ ์ฐจ์ด๋ฅผ ์ดํดํ๊ณ ์ด ๊ฐ์ด๋์ ์ค๋ช
๋ ๊ธฐ์ ์ ์ ์ฉํจ์ผ๋ก์จ React ์ ํ๋ฆฌ์ผ์ด์
์ ์ฑ๋ฅ๊ณผ ๋ฐ์์ฑ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์ต๋๋ค. ์ ํ๋ฆฌ์ผ์ด์
์ ํ๋กํ์ผ๋งํ์ฌ ์ฑ๋ฅ ๋ณ๋ชฉ ํ์์ ์๋ณํ๊ณ , ํน์ ์๊ตฌ์ ๊ฐ์ฅ ์ ํฉํ ์ต์ ํ ์ ๋ต์ ์ ํํ๋ ๊ฒ์ ์์ง ๋ง์ธ์. ์ค์ ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ํ์ธํ์ง ์๊ณ ์ฃ๋ถ๋ฆฌ ์ต์ ํํ์ง ๋ง์ญ์์ค. ๋จผ์ ๊น๋ํ๊ณ ์ ์ง๋ณด์ ๊ฐ๋ฅํ ์ฝ๋๋ฅผ ์์ฑํ๋ ๋ฐ ์ง์คํ๊ณ , ํ์์ ๋ฐ๋ผ ์ต์ ํํ์ธ์. ํต์ฌ์ ์ฑ๋ฅ๊ณผ ์ฝ๋ ๊ฐ๋
์ฑ ์ฌ์ด์ ๊ท ํ์ ๋ง์ถ๋ ๊ฒ์
๋๋ค.